import 'dart:convert';
import 'dart:io';

typedef EventHandle<T> = void Function(UdpSocketEvent<T> event);

enum EventType { msg }

class UdpSocket {
  RawDatagramSocket? _socket;
  int _port = 9999;
  Function(RawSocketEvent event)? onData;
  Function(Object error)? onError;
  Function? onDone;
  Function? onClose;
  Function? onReady;

  List<EventTypeHandle> eventHandleList = [];

  eventHandle(RawSocketEvent event) {
    onData != null && onData!(event);
    switch (event) {
      case RawSocketEvent.read:
        final datagram = _socket?.receive();
        if (datagram != null) {
          final message = String.fromCharCodes(datagram.data);
          final sender = SenderData(datagram.address.address, datagram.port);
          final msgEvent = UdpSocketEvent(EventType.msg, MsgData(message, sender));
          for (var handle in eventHandleList) {
            if (handle.type == EventType.msg) {
              handle.handle(msgEvent);
            }
          }
        }
        break;
      case RawSocketEvent.write:
        // 无需处理写事件，因为我们只接收消息
        break;
      case RawSocketEvent.closed:
        onClose != null && onClose!();
        break;
      case RawSocketEvent.readClosed:
        // 无需处理
        break;
    }
  }

  UdpSocket({
    int port = 9999,
    this.onData,
    this.onError,
    this.onDone,
    this.onReady,
    this.onClose,
  }) {
    _port = port;
    init();
  }
  init() async {
    _socket = await RawDatagramSocket.bind(InternetAddress.anyIPv4, _port) as RawDatagramSocket?;
    // 设置套接字选项以接收广播消息
    _socket!.broadcastEnabled = true;
    _socket!.multicastLoopback = true;
    _socket?.listen(eventHandle, onError: onError, onDone: () {
      onDone != null && onDone!();
    });
    onReady != null && onReady!();
  }

  addEventListener<T>(EventType type, EventHandle<T> handle) {
    eventHandleList.add(EventTypeHandle<T>(type, handle));
  }

  removeEventListener<T>(EventType? type, EventHandle<T>? handle) {
    if (type == null) {
      eventHandleList.clear();
    } else if (handle == null) {
      eventHandleList.removeWhere((eventHandle) {
        return eventHandle.type == type;
      });
    } else {
      eventHandleList.removeWhere((eventHandle) {
        return eventHandle.type == type && eventHandle.handle == handle;
      });
    }
  }

  sendBroadcast(String msg) {
    _socket?.send(utf8.encode(msg), InternetAddress('255.255.255.255'), _port);
  }

  close() {
    _socket?.close();
    onClose != null && onClose!();
  }
}

class EventTypeHandle<T> {
  EventType type;
  EventHandle<T> handle;

  EventTypeHandle(this.type, this.handle);
}

class UdpSocketEvent<T> {
  EventType type;
  T data;
  UdpSocketEvent(this.type, this.data);
}

class MsgData {
  String msg;
  SenderData sender;
  MsgData(this.msg, this.sender);
}

class SenderData {
  String ip;
  int port;
  SenderData(this.ip, this.port);
}
